package com.oujia.cloud.cloudcommon.util;

import com.alibaba.fastjson.JSON;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.HttpClientUtils;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.security.KeyStore;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 
 * @Desc HttpClient工具类，同步发送
 *
 * @Author daijunjie
 * @DateTime 2015年10月12日 下午2:15:31
 *
 */
public class HttpClientUtil {

	private static final Log logger = LogFactory.getLog(HttpClientUtil.class);

	public static String CHARSET = "UTF-8";
	/** 默认HttpClient */
	private static volatile CloseableHttpClient defaultClient;
	/** 代理HttpClient */
	private static volatile CloseableHttpClient proxyClient;
	/** 无代理HttpClient */
	private static volatile CloseableHttpClient noProxyClient;

	public static class HttpConfig {
		/** 是否代理模式 */
		private boolean proxyMode;
		/** 代理主机地址 */
		private String proxyHost;
		/** 代理端口 */
		private int proxyPort;
		/** 连接池最大连接数（所有路由最大连接数相加不能超过） */
		private int maxTotal;
		/** 每个路由最大连接数 */
		private int maxPerRoute;
		/** socket超时时间，单位毫秒 */
		private int socketTimeout;
		/** 连接超时时间，单位毫秒 */
		private int connectTimeout;
		/** 连接请求超时时间，单位毫秒 */
		private int connectionRequestTimeout;
		/** 证书文件路径 */
		private String certFilePath;
		/** 证书密码 */
		private String certPassword;
		/** 证书格式 X509、PKCS12、JKS */
		private String certType;

		/** 最大连接数 */
		public final static int MAXTOTAL = 50000;
		/** 每个链路最大连接数 */
		public final static int MAXPERROUTE = 10000;
		/** Socket超时时间 */
		public final static int SOCKETTIMEOUT = 10000;
		/** 连接超时时间 */
		public final static int CONNECTTIMEOUT = 10000;
		/** 连接请求超时时间 */
		public final static int CONNECTIONREQUESTTIMEOUT = 10000;

		/** 是否代理 */
		public final static String PROP_PROXYMODE = "http.proxyMode";
		/** 代理地址属性 */
		public final static String PROP_PROXYHOST = "http.proxyHost";
		/** 代理端口属性 */
		public final static String PROP_PROXYPORT = "http.proxyPort";
		/** 最大连接数属性 */
		public final static String PROP_MAXTOTAL = "http.maxTotal";
		/** 每个链路最大连接数属性 */
		public final static String PROP_MAXPERROUTE = "http.maxPerRoute";
		/** Socket超时时间属性 */
		public final static String PROP_SOCKETTIMEOUT = "http.socketTimeout";
		/** 连接超时时间属性 */
		public final static String PROP_CONNECTTIMEOUT = "http.connectTimeout";
		/** 连接请求超时时间属性 */
		public final static String PROP_CONNECTIONREQUESTTIMEOUT = "http.connectionRequestTimeout";

		public boolean isProxyMode() {
			return proxyMode;
		}

		public void setProxyMode(boolean proxyMode) {
			this.proxyMode = proxyMode;
		}

		public String getProxyHost() {
			return proxyHost;
		}

		public void setProxyHost(String proxyHost) {
			this.proxyHost = proxyHost;
		}

		public int getProxyPort() {
			return proxyPort;
		}

		public void setProxyPort(int proxyPort) {
			this.proxyPort = proxyPort;
		}

		public int getMaxTotal() {
			return maxTotal;
		}

		public void setMaxTotal(int maxTotal) {
			this.maxTotal = maxTotal;
		}

		public int getMaxPerRoute() {
			return maxPerRoute;
		}

		public void setMaxPerRoute(int maxPerRoute) {
			this.maxPerRoute = maxPerRoute;
		}

		public int getSocketTimeout() {
			return socketTimeout;
		}

		public void setSocketTimeout(int socketTimeout) {
			this.socketTimeout = socketTimeout;
		}

		public int getConnectTimeout() {
			return connectTimeout;
		}

		public void setConnectTimeout(int connectTimeout) {
			this.connectTimeout = connectTimeout;
		}

		public int getConnectionRequestTimeout() {
			return connectionRequestTimeout;
		}

		public void setConnectionRequestTimeout(int connectionRequestTimeout) {
			this.connectionRequestTimeout = connectionRequestTimeout;
		}

		public String getCertFilePath() {
			return certFilePath;
		}

		public void setCertFilePath(String certFilePath) {
			this.certFilePath = certFilePath;
		}

		public String getCertPassword() {
			return certPassword;
		}

		public void setCertPassword(String certPassword) {
			this.certPassword = certPassword;
		}

		public String getCertType() {
			return certType;
		}

		public void setCertType(String certType) {
			this.certType = certType;
		}

	}

	/**
	 * 默认配置，如果系统配置了代理则包括代理
	 * 
	 * @return
	 */
	public static HttpConfig getDefaultConfig() {
		HttpConfig config = getNoProxyConfig();
		String proxyMode = System.getProperty(HttpConfig.PROP_PROXYMODE);
		if ("true".equals(proxyMode)) {
			config.setProxyMode(true);
			config.setProxyHost(System.getProperty(HttpConfig.PROP_PROXYHOST));
			config.setProxyPort(Integer.parseInt(System.getProperty(HttpConfig.PROP_PROXYPORT)));
		}
		return config;
	}

	/**
	 * 获取代理配置
	 * 
	 * @return
	 */
	public static HttpConfig getProxyConfig(String proxyHost, int proxyPort) {
		HttpConfig config = getNoProxyConfig();
		config.setProxyMode(true);
		config.setProxyHost(proxyHost);
		config.setProxyPort(proxyPort);
		return config;
	}

	/**
	 * 获取无代理配置
	 * 
	 * @return
	 */
	public static HttpConfig getNoProxyConfig() {
		HttpConfig config = new HttpConfig();
		config.setMaxTotal(Integer.parseInt(System.getProperty(HttpConfig.PROP_MAXTOTAL, HttpConfig.MAXTOTAL + "")));
		config.setMaxPerRoute(
				Integer.parseInt(System.getProperty(HttpConfig.PROP_MAXPERROUTE, HttpConfig.MAXPERROUTE + "")));
		config.setSocketTimeout(
				Integer.parseInt(System.getProperty(HttpConfig.PROP_SOCKETTIMEOUT, HttpConfig.SOCKETTIMEOUT + "")));
		config.setConnectTimeout(
				Integer.parseInt(System.getProperty(HttpConfig.PROP_CONNECTTIMEOUT, HttpConfig.CONNECTTIMEOUT + "")));
		config.setConnectionRequestTimeout(Integer.parseInt(System.getProperty(HttpConfig.PROP_CONNECTIONREQUESTTIMEOUT,
				HttpConfig.CONNECTIONREQUESTTIMEOUT + "")));
		return config;
	}

	/**
	 * 创建HttpClient
	 * 
	 * 默认设置，如果系统变量配置了代理，则会走代理
	 * 
	 * @return
	 */
	public static CloseableHttpClient getDefaultHttpClient() {
		if (defaultClient == null) {
			synchronized (HttpClientUtil.class) {
				if (defaultClient == null) {
					defaultClient = createHttpClient(getDefaultConfig());
				}
			}
		}
		return defaultClient;
	}

	/**
	 * 创建代理HttpClient
	 * 
	 * @return
	 */
	public static CloseableHttpClient getProxyHttpClient(String proxyHost, int proxyPort) {
		if (proxyClient == null) {
			synchronized (HttpClientUtil.class) {
				if (proxyClient == null) {
					proxyClient = createHttpClient(getProxyConfig(proxyHost, proxyPort));
				}
			}
		}
		return proxyClient;
	}

	/**
	 * 创建无代理HttpClient
	 * 
	 * @return
	 */
	public static CloseableHttpClient getNoProxyHttpClient() {
		if (noProxyClient == null) {
			synchronized (HttpClientUtil.class) {
				if (noProxyClient == null) {
					noProxyClient = createHttpClient(getNoProxyConfig());
				}
			}
		}
		return noProxyClient;
	}

	/**
	 * 创建HttpClient，根据配置参数HttpConfig，可设置http代理、HTTPS请求
	 * 
	 * @param config
	 * @return
	 * @throws Exception
	 */
	public static CloseableHttpClient createHttpClient(HttpConfig config) {
		RequestConfig.Builder builder = RequestConfig.custom();
		// socket超时，单位毫秒
		if (config.getSocketTimeout() > 0) {
			builder.setSocketTimeout(config.getSocketTimeout());
		}
		// 连接超时时间，单位毫秒
		if (config.getConnectTimeout() > 0) {
			builder.setConnectTimeout(config.getConnectTimeout());
		}
		// 连接请求超时时间，单位毫秒
		if (config.getConnectionRequestTimeout() > 0) {
			builder.setSocketTimeout(config.getConnectionRequestTimeout());
		}
		// 代理主机地址,代理端口
		if (config.isProxyMode()) {
			builder.setProxy(new HttpHost(config.getProxyHost(), config.getProxyPort()));
		}
		PoolingHttpClientConnectionManager connManager = null;
		if (config.getCertFilePath() == null) {
			connManager = new PoolingHttpClientConnectionManager();
		} else {
			// Https SSL连接
			InputStream instream = null;
			try {
				instream = new FileInputStream(new File(config.getCertFilePath()));// 证书路径
				KeyStore keyStore = KeyStore.getInstance(config.getCertType());// 证书类型
				keyStore.load(instream, config.getCertPassword().toCharArray());// 证书密码

				SSLContext sslContext = SSLContexts.custom()
						.loadKeyMaterial(keyStore, config.getCertPassword().toCharArray()).build();

				SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext,
						new String[] { "TLSv1" }, null, SSLConnectionSocketFactory.getDefaultHostnameVerifier());

				Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder
						.<ConnectionSocketFactory>create()//
						.register("http", PlainConnectionSocketFactory.getSocketFactory())//
						.register("https", sslSocketFactory).build();
				// 初始化连接管理器
				connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
			} catch (Exception e) {
				throw new RuntimeException(e.getMessage(), e);
			} finally {
				IOUtils.closeQuietly(instream);
			}
		}

		// 连接池最大连接数（所有路由最大连接数相加不能超过）
		if (config.getMaxTotal() > 0) {
			connManager.setMaxTotal(config.getMaxTotal());
		}
		// 连接池每个路由最大连接数
		if (config.getMaxPerRoute() > 0) {
			connManager.setDefaultMaxPerRoute(config.getMaxPerRoute());
		}
		RequestConfig requestConfig = builder.build();
		CloseableHttpClient client = HttpClients.custom()//
				.setConnectionManager(connManager)//
				.setRetryHandler(new DefaultHttpRequestRetryHandler(0, false))// 设置不重发
				.setDefaultRequestConfig(requestConfig)//
				.build();
		return client;

	}

	/**
	 * 关闭httpClient连接
	 * 
	 * @param httpClient
	 */
	public static void closeHttpClient(CloseableHttpClient httpClient) {
		HttpClientUtils.closeQuietly(httpClient);
		httpClient = null;
	}

	/**
	 * 使用默认HttpClient发送GET请求
	 * 
	 * @param url
	 * @return
	 * @throws Exception
	 */
	public static String get(String url) throws Exception {
		return get(getDefaultHttpClient(), url, null);
	}

	/**
	 * 发送GET请求
	 * 
	 * @param client
	 * @param url
	 * @return
	 * @throws Exception
	 */
	public static String get(CloseableHttpClient client, String url) throws Exception {
		return get(client, url, null);
	}

	/**
	 * 使用默认HttpClient发送GET请求
	 * 
	 * @param url
	 * @param params
	 * @return
	 * @throws Exception
	 */
	public static String get(String url, Map<String, String> params) throws Exception {
		return get(getDefaultHttpClient(), url, params);
	}

	/**
	 * 发送GET请求
	 * 
	 * @param client
	 * @param url
	 * @param params
	 * @return
	 * @throws Exception
	 */
	public static String get(CloseableHttpClient client, String url, Map<String, String> params) throws Exception {
		CloseableHttpResponse response = null;
		try {
			if (params != null && params.size() > 0) {
				if (!url.contains("?")) {
					url += "?1=1";
				}
				for (Map.Entry<String, String> entry : params.entrySet()) {
					// url += "&" + entry.getKey() + "=" + entry.getValue();
					url += "&" + entry.getKey() + "=" + URLEncoder.encode(entry.getValue(), CHARSET);
				}
			}
			HttpGet request = new HttpGet(url);
			request.addHeader(HttpHeaders.CONTENT_ENCODING, CHARSET);
			request.addHeader("content-type", "application/json");
			response = client.execute(request);
			int statusCode = response.getStatusLine().getStatusCode();
			if (statusCode != 200) {
				request.abort();
				throw new IOException("Http request response error:" + response.getStatusLine());
			}
			HttpEntity entity = response.getEntity();
			String result = null;
			if (entity != null) {
				result = EntityUtils.toString(entity, CHARSET);
			}
			return result;
		} finally {
			// 包含 EntityUtils.consume(entity);
			HttpClientUtils.closeQuietly(response);
		}
	}

	/**
	 * 使用默认HttpClient发送POST请求
	 * 
	 * @param url
	 * @param params
	 * @return
	 * @throws Exception
	 */
	public static String post(String url, Map<String, String> params) throws Exception {
		return post(getDefaultHttpClient(), url, params);
	}

	/**
	 * 发送POST请求
	 * 
	 * @param client
	 * @param url
	 * @param params
	 * @return
	 * @throws Exception
	 */
	public static String post(CloseableHttpClient client, String url, Map<String, String> params) throws Exception {
		CloseableHttpResponse response = null;
		try {
			HttpPost request = new HttpPost(url);
			request.addHeader(HttpHeaders.CONTENT_ENCODING, CHARSET);
			if (params != null && params.size() > 0) {
				List<BasicNameValuePair> nvps = new ArrayList<BasicNameValuePair>();
				for (Map.Entry<String, String> entry : params.entrySet()) {
					nvps.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
				}
				request.setEntity(new UrlEncodedFormEntity(nvps, CHARSET));
			}
			response = client.execute(request);
			int statusCode = response.getStatusLine().getStatusCode();
			if (statusCode != 200) {
				request.abort();
				throw new IOException("Http request response error:" + response.getStatusLine());
			}
			HttpEntity entity = response.getEntity();
			String result = null;
			if (entity != null) {
				result = EntityUtils.toString(entity, CHARSET);
			}
			return result;
		} finally {
			// 包含 EntityUtils.consume(entity);
			HttpClientUtils.closeQuietly(response);
		}
	}

	/**
	 * 使用默认HttpClient执行一个HTTP POST JSON请求
	 * 
	 * @param url
	 * @param requestObject
	 * @param responseClazz
	 * @return
	 * @throws Exception
	 */
	public static <T> T postJson(String url, Object requestObject, Class<T> responseClazz) throws Exception {
		return postJson(getDefaultHttpClient(), url, requestObject, responseClazz);
	}

	/**
	 * 执行一个HTTP POST JSON请求
	 * 
	 * @param client
	 * @param url
	 * @param requestObject
	 * @param responseClazz
	 * @return
	 * @throws Exception
	 */
	public static <T> T postJson(CloseableHttpClient client, String url, Object requestObject, Class<T> responseClazz)
			throws Exception {
		String reqJson = null;
		if (requestObject != null) {
			if (requestObject instanceof String) {
				reqJson = (String) requestObject;
			} else {
				reqJson = JSON.toJSONString(requestObject);
			}
		}
		String ret = postJson(client, url, reqJson);
		if (ret == null) {
			return null;
		}
		if (String.class.isAssignableFrom(responseClazz)) {
			return (T) ret;
		}
		return JSON.parseObject(ret, responseClazz);
	}

	/**
	 * 执行一个HTTP POST JSON请求
	 * 
	 * @param client
	 * @param url
	 * @param json
	 * @return
	 * @throws Exception
	 */
	public static String postJson(CloseableHttpClient client, String url, String json) throws Exception {
		return post(client, url, json, "application/json");
	}

	/**
	 * 执行一个HTTP POST XML请求
	 * 
	 * @param client
	 * @param url
	 * @param xml
	 * @return
	 * @throws Exception
	 */
	public static String postXml(CloseableHttpClient client, String url, String xml) throws Exception {
		return post(client, url, xml, "text/xml");
	}

	/**
	 * 执行一个HTTP POST JSON请求
	 * 
	 * @param client
	 * @param url
	 * @return
	 * @throws Exception
	 */
	public static String post(CloseableHttpClient client, String url, String data, String contentType)
			throws Exception {
		CloseableHttpResponse response = null;
		HttpPost request = null;
		try {
			request = new HttpPost(url);
			if (StringUtils.isNotEmpty(contentType)) {
				request.addHeader(HttpHeaders.CONTENT_TYPE, contentType);
			}
			request.addHeader(HttpHeaders.CONTENT_ENCODING, CHARSET);
			if (StringUtils.isNotEmpty(data)) {
				request.setEntity(new StringEntity(data, CHARSET));
			}
			response = client.execute(request);
			int statusCode = response.getStatusLine().getStatusCode();
			if (statusCode != 200) {
				request.abort();
				throw new IOException("Http request response error:" + response.getStatusLine());
			}
			HttpEntity entity = response.getEntity();
			String result = null;
			if (entity != null) {
				result = EntityUtils.toString(entity, CHARSET);
			}
			logger.info("请求URL[" + url + "]，参数[" + data + "]，响应[" + result + "]");
			return result;
		} finally {
			// 包含 EntityUtils.consume(entity);
			HttpClientUtils.closeQuietly(response);
		}
	}

	/**
	 * 上传文件
	 * 
	 * @param url
	 * @param params
	 *            类似表单参数，SpringMVC 也要设置相应参数
	 * @param file
	 * @return
	 * @throws Exception
	 */
	public static String uploadFile(String url, Map<String, String> params, File file) throws Exception {
		return uploadFile(getDefaultHttpClient(), url, params, file);
	}

	/**
	 * 上传文件
	 * 
	 * @param client
	 * @param url
	 * @param params
	 *            类似表单参数，SpringMVC 也要设置相应参数
	 * @param file
	 * @return
	 * @throws Exception
	 */
	public static String uploadFile(CloseableHttpClient client, String url, Map<String, String> params, File file)
			throws Exception {
		CloseableHttpResponse response = null;
		HttpPost request = null;
		try {
			request = new HttpPost(url);
			request.addHeader(HttpHeaders.CONTENT_ENCODING, CHARSET);
			MultipartEntityBuilder builder = MultipartEntityBuilder.create();
			if (file != null) {
				builder.addPart("file", new FileBody(file));
			}
			if (params != null) {
				for (Map.Entry<String, String> entry : params.entrySet()) {
					builder.addPart(entry.getKey(), new StringBody(entry.getValue(), ContentType.TEXT_PLAIN));
				}
			}
			request.setEntity(builder.build());
			response = client.execute(request);
			int statusCode = response.getStatusLine().getStatusCode();
			if (statusCode != 200) {
				request.abort();
				throw new IOException("Http request response error:" + response.getStatusLine());
			}
			HttpEntity entity = response.getEntity();
			String result = null;
			if (entity != null) {
				result = EntityUtils.toString(entity, CHARSET);
			}
			return result;
		} finally {
			// 包含 EntityUtils.consume(entity);
			HttpClientUtils.closeQuietly(response);
		}
	}

}
